package com.iwjw.ibatis.generate.util;

import com.iwjw.ibatis.entity.ColumnInfo;
import com.iwjw.ibatis.entity.TableInfo;
import com.iwjw.ibatis.generate.model.CustomizeTableInfo;
import com.iwjw.ibatis.generate.model.FieldInfo;
import com.iwjw.ibatis.generate.model.GenerateConfig;
import com.iwjw.ibatis.mapper.ISqlInjector;
import com.iwjw.ibatis.util.CamelCaseUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.sql.Connection;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * @author: huangweiwei
 * @email: ihuangweiwei@163.com
 * @created:2016/10/25
 */
public class BuildModelUtil {
    public static final String ENTITY_TEMPLATE = "" +
            "package %s;\n" +
            "\n" +
            "%s\n" +
            "import com.iwjw.ibatis.annotations.Column;\n" +
            "import com.iwjw.ibatis.annotations.PrimaryKey;\n" +
            "import com.iwjw.ibatis.annotations.Table;\n" +
            "import lombok.Data;\n" +
            "\n" +
            "/**\n" +
            " * @author %s\n" +
            " * @Time %s\n" +
            " */\n" +
            "@Data\n" +
            "@Table(%s)\n" +
            "public class %s {\n" +
            "%s\n" +
            "%s\n" +
            "}\n";
    public static final String PROPERTY_TEMPLATE = "%s\tprivate %s %s;\n";
    private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd");
    public static Logger logger = LoggerFactory.getLogger(BuildModelUtil.class);

    public static boolean build(String pkg, String tableName, CustomizeTableInfo customizeTableInfo, Connection connection) {
        TableInfo tableInfo = buildTableInfo(tableName, BuildTableInfoUtil.getFieldInfos(tableName, connection), customizeTableInfo);
        String code = build(customizeTableInfo,pkg, buildImport(tableInfo.getColumnInfoList()), tableName, GenerateConfig.XML_BUILD ? ISqlInjector.DEFAULT_RESULT_MAP : "", buildQuery(tableInfo.getColumnInfoList()), build(tableInfo.getColumnInfoList(), tableInfo.getPrimary()));
        logger.info("model:\n{}", code);
        String model = CamelCaseUtils.toCapitalizeCamelCase(tableName);
        if (GenerateConfig.MAPPER_BUILD) {
            if (customizeTableInfo != null && !StringUtils.isEmpty(customizeTableInfo.getTableProperty())) {
                model = CamelCaseUtils.toCapitalizeCamelCase(customizeTableInfo.getTableProperty());
            }
        }
        return FileUtil.write(pkg, model+ ".java", code, GenerateConfig.OVERRIDE_FILE);
    }

    private static String build(List<ColumnInfo> columnInfoList, ColumnInfo primaryColumn) {
        if (columnInfoList == null || columnInfoList.isEmpty()) {
            return "";
        }
        StringBuffer stringBuffer = new StringBuffer();

        for (ColumnInfo columnInfo : columnInfoList) {
            if (columnInfo.getName().equals(GenerateConfig.OPTIMISTIC_KEY)) {
                stringBuffer.append("\t@com.iwjw.ibatis.annotations.OptimisticKey\n");
            }
            stringBuffer.append(buildPropertyInfo(columnInfo, columnInfo.equals(primaryColumn)));
        }
        return stringBuffer.toString();


    }

    private static TableInfo buildTableInfo(String tableName, List<FieldInfo> fieldInfoList, CustomizeTableInfo customizeTableInfo) {
        TableInfo tableInfo = new TableInfo();
        List<ColumnInfo> columnInfos = new ArrayList<ColumnInfo>();
        tableInfo.setColumnInfoList(columnInfos);
        tableInfo.setName(tableName);

        //TODO 表名生成
//        if(!StringUtils.isEmpty(customizeTableInfo.getTableProperty())){

//        }
        Map<String, CustomizeTableInfo.ColumnOverrid> columnOverridMap = build(customizeTableInfo);
        for (FieldInfo fieldInfo : fieldInfoList) {
            ColumnInfo columnInfo = new ColumnInfo();
            columnInfo.setComment(fieldInfo.getComment().trim());
            columnInfo.setName(fieldInfo.getField());
            columnInfo.setProperty(fieldInfo.getField());
            columnInfo.setJdbcType(fieldInfo.getType().toUpperCase());
            columnInfo.setJavaType(BuildTableInfoUtil.mysqlProcessType(fieldInfo.getType()));
            CustomizeTableInfo.ColumnOverrid columnOverrid = columnOverridMap.get(columnInfo.getName());
            if (columnOverrid != null) {
                if (!StringUtils.isEmpty(columnOverrid.getProperty())) {
                    columnInfo.setProperty(columnOverrid.getProperty());
                }
                if (columnOverrid.getJavaType() != null) {
                    columnInfo.setJavaType(columnOverrid.getJavaType());
                }
                if (!StringUtils.isEmpty(columnOverrid.getJdbcType())) {
                    columnInfo.setJdbcType(columnOverrid.getJdbcType());
                }
                if (columnOverrid.getTypeHandler() != null) {
                    columnInfo.setTypeHandler(columnOverrid.getTypeHandler());
                }
            }
            columnInfos.add(columnInfo);
            if (fieldInfo.getKey().equals("PRI")) {
                tableInfo.setPrimary(columnInfo);
                if (fieldInfo.getExtra().startsWith("auto")) {
                    tableInfo.setIsAuto(Boolean.TRUE);
                }
            }
            if (fieldInfo.getField().equals(GenerateConfig.OPTIMISTIC_KEY)) {
                tableInfo.setOptimistic(columnInfo);
            }
        }
        return tableInfo;
    }

    private static Map<String, CustomizeTableInfo.ColumnOverrid> build(CustomizeTableInfo tableInfo) {
        if (tableInfo == null) {
            return Collections.emptyMap();
        }
        Map<String, CustomizeTableInfo.ColumnOverrid> map = new HashMap<String, CustomizeTableInfo.ColumnOverrid>();
        if (tableInfo.getColumnOverridList() == null) {
            return Collections.emptyMap();
        }
        List<CustomizeTableInfo.ColumnOverrid> list = tableInfo.getColumnOverridList();
        for (CustomizeTableInfo.ColumnOverrid columnOverrid : list) {
            map.put(columnOverrid.getName(), columnOverrid);
        }

        return map;
    }

    public static String build(CustomizeTableInfo customizeTableInfo,String pkg, String importInfo, String tableName, String resultMap, String columnField, String privateCode) {
        String model = CamelCaseUtils.toCapitalizeCamelCase(tableName);
        if (GenerateConfig.MAPPER_BUILD) {
            if (customizeTableInfo != null && !StringUtils.isEmpty(customizeTableInfo.getTableProperty())) {
                model = CamelCaseUtils.toCapitalizeCamelCase(customizeTableInfo.getTableProperty());
            }
        }
        String code = String.format(ENTITY_TEMPLATE, pkg, importInfo, GenerateConfig.AUTHOR, dateFormat.format(new Date()), buildTableAnation(tableName, resultMap), model, columnField, privateCode).trim();
        return code;
    }

    private static String buildTableAnation(String tableName, String resultMap) {
        if (StringUtils.isEmpty(resultMap)) {
            return "name = \"" + tableName + "\"";
        }
        return "name = \"" + tableName + "\", resultMap=\"" + resultMap + "\"";
    }

    /**
     * 构建import信息
     *
     * @param columnInfoList
     * @return
     */
    public static String buildImport(List<ColumnInfo> columnInfoList) {
        if (columnInfoList == null || columnInfoList.isEmpty()) {
            return "";
        }
        Set<String> importSet = new HashSet();
        for (ColumnInfo columnInfo : columnInfoList) {
            importSet.add("import " + columnInfo.getJavaType().getName() + ";\n");
        }
        StringBuffer importStr = new StringBuffer();
        for (String str : importSet) {
            importStr.append(str);
        }
        return importStr.toString().trim();
    }

    /**
     * 构建查询词
     *
     * @param columnInfoList
     * @return
     */
    public static String buildQuery(List<ColumnInfo> columnInfoList) {
        if (columnInfoList == null || columnInfoList.isEmpty()) {
            return "";
        }
        StringBuffer queryInfo = new StringBuffer();

        for (ColumnInfo columnInfo : columnInfoList) {
            if (!StringUtils.isEmpty(columnInfo.getComment())) {
                queryInfo.append("\t/** " + columnInfo.getComment() + " */\n");
            }
            queryInfo.append("\tpublic static final String " + CamelCaseUtils.toUnderlineName(columnInfo.getProperty()).toUpperCase() + " = \"" + columnInfo.getName() + "\";\n");
        }
        return queryInfo.toString();
    }

    /**
     * 构建实例信息
     *
     * @param columnInfo
     * @param isPrimary
     * @return
     */
    public static String buildPropertyInfo(ColumnInfo columnInfo, boolean isPrimary) {
        String comment = columnInfo.getComment();
        StringBuffer columnAnnotations = new StringBuffer();
        if (!StringUtils.isEmpty(columnInfo.getProperty()) && !columnInfo.getProperty().equals(columnInfo.getName())) {
            columnAnnotations.append("name = \"" + columnInfo.getName() + "\" , ");
        }
        if (columnInfo.getTypeHandler() != null && !columnInfo.getTypeHandler().isInterface()) {
            columnAnnotations.append(" typeHandler = " + columnInfo.getTypeHandler().getName() + ".class ,");
        }
        if (!StringUtils.isEmpty(columnInfo.getJdbcType())) {
            columnAnnotations.append(" jdbcType = \"" + columnInfo.getJdbcType() + "\",");
        }
        String columnInfoStr = "";
        if (columnAnnotations.toString().length() > 0) {
            String str = columnAnnotations.toString();
            columnInfoStr = str.substring(0, str.length() - 1);
        }
        return buildPropertyInfo(comment, isPrimary, columnInfoStr, columnInfo.getJavaType().getSimpleName(), columnInfo.getProperty());
    }


    private static String buildPropertyInfo(String comment, Boolean isPrimary, String columnAnnotation, String clazz, String property) {
        if (!StringUtils.isEmpty(comment)) {
            comment = "\t/** " + comment + " */\n";
        }
        String primaryCode = "";
        if (isPrimary) {
            primaryCode = "\t@PrimaryKey\n";
        }
        if (!StringUtils.isEmpty(columnAnnotation)) {
            columnAnnotation = "\t@Column(" + columnAnnotation.trim() + ")\n";
        }
        String info = "\t" + (comment + primaryCode + columnAnnotation).trim() + "\n";
        return String.format(PROPERTY_TEMPLATE, info, clazz, property);
    }


}
